package com.example.helloandroid;

import java.util.ArrayList;

import com.motorola.blur.service.blur.Actions;
import com.motorola.blur.service.blur.WSResponse;
import com.motorola.blur.service.blur.ErrorTranslator.ErrorCodes;
import com.motorola.blur.service.blur.devicesetup.ProviderCredentials;
import com.motorola.blur.service.blur.devicesetup.RemoveProviderCredentialsWS;
import com.motorola.blur.service.blur.devicesetup.client.DeviceSetupWSClientFactory;
import com.motorola.blur.service.blur.devicesetup.client.IDeviceSetupWSClient;
import com.motorola.blur.service.snpsettings.protocol.SnpErrors.SNPError;
import com.motorola.blur.service.snpsettings.protocol.SnpSettingsProtocol.SNPResponse;
import com.motorola.blur.util.Logger;
import com.motorola.blur.util.os.MyLooper;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.database.sqlite.SQLiteDatabase;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;


public class BlurSnAccountRemovalHelper {
    private static final String TAG = "BlurSnAccountRemovalHelper";
    private static final String PREFS_NAME = "SnAccountsRemoteRemoval";
    private static final String PREFS_KEY_ALL_ACCOUNTS = "All_ACCOUNTS";
    private static final int TOTAL_RETRY_LIMIT = 500;
    private static final int SESSION_RETRY_LIMIT = 5;
    private static final int RETRY_DELAY = 30 * 1000;

    private static final int MSG_START_REMOTE_REMOVE = 1;
    private static final int MSG_FINISH_REMOTE_REMOVE = 2;
    private static final int MSG_REMOTE_RESPONSE = 3;

    private static BlurSnAccountRemovalHelper sRemovalHelper;

    private Context mContext;
    private Handler mHandler;
    private SnAccount[] mSnAccounts;
    private int mCurAccount;
    private BroadcastReceiver mRemoteRemoveReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            Message msg = mHandler.obtainMessage(MSG_REMOTE_RESPONSE);
            msg.obj = intent;
            mHandler.dispatchMessage(msg);
        }
    };

    private BlurSnAccountRemovalHelper(Context ctx) {
        mContext = ctx;
    }

    public static void upgradeFromVersion18toVersion19(Context ctx, SQLiteDatabase db) {
        // re-entry check
        if (sRemovalHelper != null) {
            return;
        }

        //TODO: temp code, to be removed
        final SharedPreferences prefs = ctx.getSharedPreferences(PREFS_NAME,
                Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = prefs.edit();
        editor.putString(PREFS_KEY_ALL_ACCOUNTS,
                "type1,name1@yahoo.com,1001 : com.motorola.blur.social.type2, name2@gmail.com, 10002");
        editor.apply();

        checkRemoveSnAccouns(ctx);
    }

    public static void checkRemoveSnAccouns(final Context ctx) {
        if (sRemovalHelper != null) {
            return;
        }

        Thread thread = new Thread(){
            @Override
            public void run() {
                if (needRemoteRemoval(ctx)) {
                    sRemovalHelper = new BlurSnAccountRemovalHelper(ctx);
                    sRemovalHelper.startRemoteRemoval();
                }
            }
        };
        thread.setPriority(Thread.MIN_PRIORITY);
        thread.start();
    }

    private static boolean needRemoteRemoval(Context ctx) {
        if (sRemovalHelper != null) {
            return false;
        }

        SnAccount[] accounts = readSnAccounts(ctx);
        if (accounts == null || accounts.length == 0) {
            return false;
        }

        return true;
    }

    /*
     * Get the locally removed Blur social accounts from local storage
     */
    private static SnAccount[] readSnAccounts(Context ctx) {
        final SharedPreferences prefs = ctx.getSharedPreferences(PREFS_NAME,
                Context.MODE_PRIVATE);
        if (prefs == null) {
            return null;
        }
        String allAccounts = prefs.getString(PREFS_KEY_ALL_ACCOUNTS, "");
        if (allAccounts == null || allAccounts.isEmpty()) {
            return null;
        }
        ArrayList<SnAccount> snAccountList = new ArrayList<SnAccount>();

        // Passe accounts
        for (String oneAccount : allAccounts.split(":")) {
            String[] arr = oneAccount.trim().split(",");
            if (arr != null & arr.length >= 3) {
                SnAccount snAccount = new SnAccount();
                snAccount.mProviderName = arr[0].trim();
                snAccount.mEmailAddress = arr[1].trim();
                snAccount.mId = Long.parseLong(arr[2].trim());
                if (arr.length >= 4) {
                    snAccount.mNumRetriedTotal = Integer.parseInt(arr[3]);
                }
                snAccountList.add(snAccount);
            }
        }

        if (snAccountList.isEmpty()) {
            return null;
        }
        SnAccount[] arrAccount = new SnAccount[snAccountList.size()];
        return snAccountList.toArray(arrAccount);
    }

    /*
     * Set the social accounts to local storage
     */
    private static boolean writeSnAccounts(Context ctx, SnAccount[] snAccounts) {
        if (snAccounts == null || snAccounts.length == 0) {
            final SharedPreferences prefs = ctx.getSharedPreferences(PREFS_NAME,
                    Context.MODE_PRIVATE);
            SharedPreferences.Editor editor = prefs.edit();
            editor.putString(PREFS_KEY_ALL_ACCOUNTS, "");
            editor.commit();
            return true;
        } else {
            StringBuffer sb = new StringBuffer();
            for (int i=0; i<snAccounts.length; i++) {
                SnAccount sa = snAccounts[i];
                if (sa.mFinished) {
                    continue;
                }
                sb.append(sa.mProviderName).append(',');
                sb.append(sa.mEmailAddress).append(',');
                sb.append(sa.mId).append(',');
                sb.append(sa.mNumRetriedTotal);
                if (i < snAccounts.length - 1) {
                    sb.append(" : ");
                }
            }

            final SharedPreferences prefs = ctx.getSharedPreferences(PREFS_NAME,
                    Context.MODE_PRIVATE);
            SharedPreferences.Editor editor = prefs.edit();
            editor.putString(PREFS_KEY_ALL_ACCOUNTS, sb.toString());
            editor.commit();
            return true;
        }
    }

    class RemoveHelperHandler extends Handler {
        public RemoveHelperHandler(Looper looper) {
            super(looper);
        }

        public void handleMessage(Message msg) {
            if (msg.what == MSG_START_REMOTE_REMOVE) {
                if (mCurAccount < 0 || mCurAccount >= mSnAccounts.length) {
                    finishRemoteRemoval();
                    return;
                }
                IntentFilter filter = new IntentFilter(RemoveProviderCredentialsWS.Response.ACTION);
                filter.addCategory("ACCOUNT_REMOVER");
                IntentFilter restartFilter = new IntentFilter(Actions.SERVICES_STARTED);
                mContext.registerReceiver(mRemoteRemoveReceiver, filter);
                mContext.registerReceiver(mRemoteRemoveReceiver, restartFilter);        
                IDeviceSetupWSClient client = DeviceSetupWSClientFactory.getClient(mContext);
                ProviderCredentials provCreds = new ProviderCredentials(
                        mSnAccounts[mCurAccount].mProviderName,
                        mSnAccounts[mCurAccount].mEmailAddress,
                        null);
                ErrorCodes error = client.removeProviderCredentials(provCreds, mSnAccounts[mCurAccount].mId,
                        "ACCOUNT_REMOVER");
                if (error != ErrorCodes.None) {
                    mContext.unregisterReceiver(mRemoteRemoveReceiver);
                }

                mSnAccounts[mCurAccount].mNumRetried ++;
                mSnAccounts[mCurAccount].mNumRetriedTotal ++;
            } else if (msg.what == MSG_FINISH_REMOTE_REMOVE) {
                finishRemoteRemoval();
            } else if (msg.what == MSG_REMOTE_RESPONSE) {
                mContext.unregisterReceiver(mRemoteRemoveReceiver);
                if (msg.obj instanceof Intent) {
                    handleRemoteResponse((Intent)msg.obj);
                }
            }
        }
    }

    protected void handleRemoteResponse(Intent intent) {
        String action = intent.getAction();
        if (RemoveProviderCredentialsWS.Response.ACTION.equals(action)) {
            RemoveProviderCredentialsWS.Response resp = intent.getParcelableExtra(intent
                    .getStringExtra(WSResponse.KEY_RESPONSE_DATA));
            ErrorCodes error = resp.getError();
            evaluateResponse(error, resp);
        } else { //Actions.SERVICES_DIED
            handleCurrentFail();
        }
    }

    private void evaluateResponse(ErrorCodes error, RemoveProviderCredentialsWS.Response resp) {
        switch (error) {
            case AccountNotFound:
            case None:
                skipCurrentAccount();
                break;
            case SNPError:
                if (resp != null) {
                    SNPResponse snpResp = resp.getSNPResponse();
                    SNPError snpError = SNPError.valueOf(snpResp.getSnpExtendedError());
                    if (snpError == SNPError.ACCOUNT_NOT_FOUND) {
                        skipCurrentAccount();
                    } else {
                        handleCurrentFail();
                    }
                } else {
                    //we should never get here!
                    Logger.e(TAG, "SNP error but no resp!");
                    handleCurrentFail();
                }
                break;
            default:
                handleCurrentFail();
                break;
        }
    }

    /*
     * Handle the response of current failure
     */
    private void handleCurrentFail() {
        if (mSnAccounts[mCurAccount].mNumRetriedTotal > TOTAL_RETRY_LIMIT) {
            mSnAccounts[mCurAccount].mFinished = true;
            writeSnAccounts(mContext, mSnAccounts);
            mCurAccount++;
        } else if (mSnAccounts[mCurAccount].mNumRetried > SESSION_RETRY_LIMIT) {
            // Will try to remove this account from server later, but not this time.
            mSnAccounts[mCurAccount].mFinished = false;
            writeSnAccounts(mContext, mSnAccounts);
            mCurAccount++;
        }

        if (mCurAccount >= mSnAccounts.length) {
            mHandler.removeMessages(MSG_FINISH_REMOTE_REMOVE);
            mHandler.sendEmptyMessage(MSG_FINISH_REMOTE_REMOVE);
        } else {
            mHandler.removeMessages(MSG_START_REMOTE_REMOVE);
            mHandler.sendEmptyMessageDelayed(MSG_START_REMOTE_REMOVE, RETRY_DELAY);
        }
    }

    /*
     * Never try to remotely remove this account again 
     */
    private void skipCurrentAccount() {
        mSnAccounts[mCurAccount].mFinished = true;
        writeSnAccounts(mContext, mSnAccounts);

        mCurAccount++;
        if (mCurAccount >= mSnAccounts.length) {
            mHandler.removeMessages(MSG_FINISH_REMOTE_REMOVE);
            mHandler.sendEmptyMessage(MSG_FINISH_REMOTE_REMOVE);
        } else {
            mHandler.removeMessages(MSG_START_REMOTE_REMOVE);
            mHandler.sendEmptyMessage(MSG_START_REMOTE_REMOVE);
        }
    }

    private void startRemoteRemoval() {
        Logger.d(TAG, "enter startAccountsRemoval");
        if (mHandler == null) {
            mHandler = new RemoveHelperHandler(MyLooper.singleton());
        }
        mSnAccounts = readSnAccounts(mContext);
        if (mSnAccounts == null || mSnAccounts.length == 0) {
            mHandler.sendEmptyMessage(MSG_FINISH_REMOTE_REMOVE);
            return;
        }
        mCurAccount = 0;
        mHandler.sendEmptyMessage(MSG_START_REMOTE_REMOVE);
    }

    private void finishRemoteRemoval() {
        writeSnAccounts(mContext, mSnAccounts);
        sRemovalHelper = null;
    }

    private static class SnAccount {
        // basic account info
        public String mProviderName;
        public String mEmailAddress;
        long mId;
        // more flags
        public int mNumRetriedTotal;
        public int mNumRetried;
        public boolean mFinished;
    }
}
